Previous Book Contents Book Index Next

Inside Macintosh: Overview /
Chapter 7 - Dialog Boxes


Using Modal Dialog Boxes

Remember that a modal dialog box puts the user into the state or "mode" of being able to work only inside the dialog box. The user cannot move the dialog box and can dismiss it only by clicking its buttons (perhaps after supplying some necessary information).

Note
The Dialog Manager also provides movable modal dialog boxes; these are modal dialog boxes that contain a title bar so that the user can drag the dialog box. You should use movable modal dialog boxes whenever the user might need to move a modal dialog box to see what it obscures or whenever you want allow the user to switch to another application while the dialog box is displayed.
In general, it's easier to create and handle simple modal dialog boxes than it is to create and handle modeless dialog boxes. The reason is that the Dialog Manager provides special routines that you can call to display alerts and other simple dialog boxes. The Dialog Manager also provides the ModalDialog procedure, which you can call to manage all user actions in modal dialog boxes.

IMPORTANT
Ease of implementation is not a sufficient reason for using modal dialog boxes instead of modeless ones. You should avoid using modal dialog boxes except when absolutely necessary.

Displaying a Modal Dialog Box

Listing 7-7 shows a standard way to display a modal dialog box. It defines the procedure DoAboutBox, which is called after the user chooses the About Venn Diagrammer command from the Apple menu.

Listing 7-7 Displaying a modal dialog box

PROCEDURE DoAboutBox (myWindow: WindowPtr);
   VAR
      myWindow:   WindowPtr;
      myDialog:   DialogPtr;
      myItem:     Integer;
BEGIN
   myWindow := FrontWindow;
   IF myWindow <> NIL THEN
      DoActivate(myWindow, 1 - activeFlag);

   myDialog := GetNewDialog(rAboutDial, NIL, WindowPtr(-1));
   IF myDialog <> NIL THEN
      BEGIN
         SetPort(myDialog);
         DoDefaultButton(myDialog);

         REPEAT
            ModalDialog(@MyModalFilter, myItem);
         UNTIL myItem = iOK;

         DisposeDialog(myDialog);
         SetPort(myWindow);
      END;
END;
When you display a modal dialog box, you should first deactivate any existing front window. The DoAboutBox procedure retrieves a window pointer to the front window and passes that pointer to the application-defined activate routine DoActivate. Then DoAboutBox calls GetNewDialog to open the dialog box specified by the resource ID rAboutDial:

CONST
   rAboutDial = 7000;         {resource ID of About dialog}
If GetNewDialog returns a dialog pointer whose value is not NIL, then DoAboutBox calls SetPort to establish the new dialog box as the current drawing port. Then it calls the application-defined procedure DoDefaultButton (defined in Listing 7-8) to draw a thick border around the default button. This indicates that the user can dismiss the dialog box by pressing the Return key or the Enter key.

Listing 7-8 Outlining the default button of a modal dialog box

PROCEDURE DoDefaultButton (myDialog: DialogPtr);
   VAR
      myType:     Integer;
      myHand:     Handle;
      myRect:     Rect;
BEGIN
   GetDialogItem(myDialog, iOK, myType, myHand, myRect);
   DoOutlineControl(myHand);
END;
The DoDefaultButton procedure simply calls the application-defined procedure DoOutlineControl to outline the dialog item whose item number is 1 (identified by the constant iOK). See page 200 for a definition of DoOutlineControl.

At this point, the modal dialog box is displayed on the screen. The DoAboutBox procedure loops indefinitely, repeatedly calling ModalDialog until the user clicks the OK button. The ModalDialog procedure handles all mouse, keystroke, and update events that occur inside the dialog box until an event involving an enabled dialog item occurs. When that happens, ModalDialog exits and returns the dialog item number in the second parameter. Your application can then do whatever is appropriate in response to an event in that item. In DoAboutBox, ModalDialog is called repeatedly until a click in the OK button occurs. At that time, the modal dialog is removed from the screen, and DoAboutBox calls SetPort to reinstate the original drawing port.

Defining a Modal Dialog Filter Function

The actions of ModalDialog are guided by the modal dialog filter function whose address is passed in its first parameter. If you pass NIL as the first parameter to the ModalDialog procedure, you'll get the standard event filtering provided by the Dialog Manager. The standard event filter function returns TRUE and causes ModalDialog to return item number 1 (the number of the default button) when the user presses the Return or the Enter key.

For most modal dialog boxes, the standard modal dialog filter function is too simple. Your application should define a modal dialog filter function that performs the following tasks:

Listing 7-9 defines a modal dialog filter function that accomplishes these tasks. In addition, the filter function MyModalFilter handles any disk-inserted events that occur while the modal dialog box is displayed.

Listing 7-9 A modal dialog filter function

FUNCTION MyModalFilter (myDialog: DialogPtr; VAR myEvent: EventRecord; 
                           VAR myItem: Integer): Boolean;
   VAR
      myType:     Integer;
      myHand:     Handle;
      myRect:     Rect;
      myKey:      Char;
      myIgnore:   LongInt;
BEGIN
   MyModalFilter := FALSE;             {assume we don't handle the event}

   CASE myEvent.what OF
      updateEvt: 
         BEGIN
            IF WindowPtr(myEvent.message) <> myDialog THEN
               DoUpdate(WindowPtr(myEvent.message));
                                    {update the window behind}
         END;
      keyDown, autoKey: 
         BEGIN
            myKey := char(BAnd(myEvent.message, charCodeMask));

            {if Return or Enter pressed, do default button}
            IF (myKey = kReturn) OR (myKey = kEnter) THEN
               BEGIN
                  GetDialogItem(myDialog, iOK, myType, myHand, myRect);
                  HiliteControl(ControlHandle(myHand), 1);
                              {make button appear to have been pressed}
                  Delay(kVisualDelay, myIgnore);
                  HiliteControl(ControlHandle(myHand), 0);
                  MyModalFilter := TRUE;
                  myItem := iOK;
               END;

            {if Escape or Cmd-. pressed, do Cancel button}
            IF (myKey = kEscape)
               OR ((myKey = kPeriod)
                     AND (BAnd(myEvent.modifiers, CmdKey) <> 0)) THEN
               BEGIN
                  GetDialogItem(myDialog, iCancel, myType, myHand, myRect);
                  HiliteControl(ControlHandle(myHand), 1);
                              {make button appear to have been pressed}
                  Delay(kVisualDelay, myIgnore);
                  HiliteControl(ControlHandle(myHand), 0);
                  MyModalFilter := TRUE;
                  myItem := iCancel;
               END;
         END;
      diskEvt: 
         BEGIN
            DoDiskEvent(myEvent);
            MyModalFilter := TRUE;        {show we've handled the event}
         END;
      OTHERWISE
         ;
   END; {CASE}
END;
An interesting part of MyModalFilter is the way it intercepts key-down events and translates them into button clicks. When, for instance, it detects that the Return key was pressed, it calls GetDialogItem to retrieve a handle to the first item in the item list (by convention, the OK button). Then MyModalFilter calls HiliteControl to invert the state of the button, waits for a specified number of ticks, and then calls HiliteControl once again to restore the button to its original state. Finally, it sets the function result and the variable parameter myItem, thus informing the calling routine that the event was handled.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
9 JUL 1996